//
// Created by vslab on 23-4-9.
//

#ifndef CODE_MSGS_H
#define CODE_MSGS_H

#ifdef DEBUG_
#define DEBUG(MESSAGE) std::cout << (MESSAGE) << std::endl;
#endif

#include <iostream>
#include <string>
#include <iomanip>
#include <vector>

namespace GNSS {
    enum sys_type{
        GN = 0,
        GP = 1,
        BD = 2
    };
    namespace msg {
        enum msg_type {
            GGA = 0,
            GSA = 1,
            GSV = 2,
            RMC = 3,
            VTG = 4,
            GGL = 5,
            NIU = 6///////////
        };
        namespace {
            char separator_ = ',';  //separator char
            char start_flag_ = '$'; //start flag
            char end_flag_ = '*';   //end flag
        }

        // UTC time
        struct UTC
        {
            int hh;
            int mm;
            int ss;
            std::string exp;
            std::string bj_time;
        };

        //latitude or longitude
        struct latlon
        {
            int dd;
            double mm;
            double exp;
        };

        struct Satellite
        {
            double elevation;
            double azimuth;
            double snr;
            void reset();
        };

        // msg base
        class Base_msg {
        public:
            using sate_num = int;
            using latlon_dir = std::string;

            Base_msg();
            ~Base_msg() { }
            virtual void parse(std::string line) = 0;   //parse this msg
            virtual bool check() = 0;   //check this msg is valid or not
            virtual double number_trans(std::string num_string);


        protected:
            std::size_t start_;
            std::size_t end_;
            std::size_t field_nums_; //the number of the field of this msg
            bool is_valid_;          //msg valid or no

        };

        // Gga
        class Gga : public Base_msg
        {
        public:
            Gga();
            virtual bool check();
            virtual void parse(std::string raw_data);
            friend std::ostream& operator<<(std::ostream &os, const Gga& msg)
            {
                static bool flag = true;
                if (flag)
                {
                    os << "Beijing Time" << separator_
                       << "Latitude" << separator_
                       << "Longitude" << std::endl;
                    flag = false;
                }
                else {
                    os.precision(11);
                    os << msg.UTC_.bj_time << separator_
                       << msg.lat_.exp << separator_
                       << msg.lon_.exp << std::endl;
                }
                return os;
            }

        private:
             UTC UTC_;   //UTC time: hhmmss.sss
             latlon lat_;   //latitude
             latlon lon_;   //longitude
             latlon_dir lat_dir_;  //direction of latitude: north, south
             latlon_dir lon_dir_; //direction of longitude: east, west
             sate_num sate_nums_; //the number of satellite being used: 00-12?

        };

        //GSV
        class Gsv : public Base_msg
        {
        public:
            using size = std::size_t;
            Gsv();
            virtual bool check();
            virtual void parse(std::string raw_data);
            friend std::ostream& operator<<(std::ostream& os, const Gsv& msg)
            {
//                os << msg.sentence_num_ << " sentences\t" << "NO." << msg.no_ << "\t"
//                   << msg.visible_sate_num_ << " visible satellites_\n" << msg.satellites_.size()
//                   << " satellites_ in this msg" << std::endl;
                if (msg.no_ == 1)
                    os << std::endl;
//                os << msg.sentence_num_ << separator_
//                   << msg.no_ << separator_
//                   << msg.visible_sate_num_ << separator_
//                   << msg.satellites_.size() << separator_ << std::endl;
                for (std::size_t i = 0; i != msg.satellites_.size(); i++)
                {
                    os << msg.satellites_.at(i).elevation << separator_
                       << msg.satellites_.at(i).azimuth << separator_;
                    if (msg.satellites_.at(i).snr > 0)
                        os << msg.satellites_.at(i).snr << std::endl;
                    else
                        os << "" << std::endl;
                }
                return os;
            }

        private:
            size sentence_num_;
            size no_;
            size visible_sate_num_;
            std::vector<Satellite> satellites_;
        };


        class Vtg : public Base_msg
        {
        public:
            Vtg();
            virtual bool check();
            virtual void parse(std::string raw_data);
            friend std::ostream& operator<<(std::ostream& os, const Vtg& msg)
            {
                static bool flag = true;
                if (flag)
                {
                    os << "Angle" << separator_
                       << "Velocity" << std::endl;
                    flag = false;
                }
                else
                {
                    os << msg.yaw_true_north_ << separator_
                       << msg.vel_ << std::endl;
                }
                return os;
            }

        private:
            double yaw_true_north_;
            double vel_;
        };
    }
}
#endif //CODE_MSGS_H
